#include <stdlib.h> /* define RAND_MAX */
#include <float.h> 
#include <engine.h>
#include "QMath.h"
#include <QVector3D>

static const double PI = 3.14159265358979323846264338327950288419717;

inline bool engine_initialize(GGame* pGame)
{
	return g_engine.EngineInitialize(pGame);
}

inline double getFps(const double elapsed)
{
	static float fps = 0;
	static float timeSinceLastUpdate = 0;
	static float frameCount = 0;
	
	frameCount++;
	timeSinceLastUpdate += elapsed;

	if(timeSinceLastUpdate > 1000.0f)
	{
		fps = frameCount / (timeSinceLastUpdate/1000.0f);
		
		frameCount = 0;
		timeSinceLastUpdate = 0;		
	}

	return fps;
}

//inline qreal lerp(const qreal v0, const qreal v1, const qreal t)
inline double lerp(const double v0, const double v1, const double t)
{
	//return (v0 * (1.0f - t) + v1 * t);
    return (v0 * (1.0f - t) + v1 * t);
}

inline QPointF bi_lerp(const QPointF& v0, const QPointF& v1, const double t)
{
    double x = lerp(v0.x(), v1.x(), t);
    double y = lerp(v0.y(), v1.y(), t);

    return QPointF(x, y);
}

inline double bspline(const double v0, const double v1, const double v2, const double v3, const double t1)  
{  
    double t2 = t1 * t1;
    double t3 = t2 * t1;  
    return ( (-1.0f*t3 + 3.0f*t2 - 3.0f*t1 + 1.0f) * v0 +
             ( 3.0f*t3 - 6.0f*t2 + 0.0f*t1 + 4.0f) * v1 +  
             (-3.0f*t3 + 3.0f*t2 + 3.0f*t1 + 1.0f) * v2 +  
             ( 1.0f*t3 + 0.0f*t2 + 0.0f*t1 + 0.0f) * v3 ) / 6.0f;  
}  

inline QPointF bi_bspline(const QPointF& v0, const QPointF& v1, const QPointF& v2, const QPointF& v3, const double t)
{
    double x = bspline(v0.x(), v1.x(), v2.x(), v3.x(), t);
    double y = bspline(v0.y(), v1.y(), v2.y(), v3.y(), t);

    return QPointF(x, y);
}

inline double getRandomMinMax(double min, double max)
{
    return min + getRandomMax(max-min);
}

inline double getRandom()
{
    return (double)rand() / (RAND_MAX + 1.0);
}

inline double getRandomMax(int max)
{
    return (double)rand() / (RAND_MAX + 1.0) * max;
}

inline double radian_to_degree(double radian)
{
    return (radian*180)/PI;
}

inline double degree_to_radian(double degree)
{
    return (degree*PI)/180;
}

//double pos_to_degree(QGraphicsItem* pItem, double toX, double toY)

double deltapos_to_degree(double current_degree, double deltaX, double deltaY)
{
	double anglePI = degree_to_radian(current_degree);
	// zero degree is up. 
	QVector3D vtFrom(qSin(anglePI), qCos(anglePI), 0);
	QVector3D vtTo(deltaX, deltaY, 0);
		
    //for window position
    vtTo.setX(-vtTo.x());

	vtFrom.normalize();
    vtTo.normalize();

	double dot = QVector3D::dotProduct(vtFrom, vtTo);
	// Oh. My God. The value of dotProduct with unit vector may be higher than 1 :(
	if(dot > 1)
		dot = 1;
	else if(dot < -1)
		dot = -1;

//    if(dot == 0)
//        qDebug() << "degree 90";
//    else if(dot > 0)
//        qDebug() << "degree less than 90";
//    if(dot < 0)
//        qDebug() << "degree more than 90";

    double anglePI2 = qAcos(dot);
    double degree = radian_to_degree(anglePI2);
    if(QVector3D::crossProduct(vtFrom, vtTo).z() >= 0)
        degree *= -1;

	//qDebug() << vtFrom << vtTo << degree;
	if(_isnan(degree) != 0)
		qDebug() << "nan";

    return degree;
}

